package pl.edu.agh.jemo.evolution.metrics.impl;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import pl.edu.agh.jemo.evolution.metrics.Metric;
import pl.edu.agh.jemo.evolution.population.Population;
import pl.edu.agh.jemo.evolution.specimen.Specimen;

/**
 * 
 * 2D metric
 * 
 */
public class MinExtremaDistanceMetric implements Metric {

	private Logger logger = Logger.getLogger(MinExtremaDistanceMetric.class);

	private static final String NAME = "MinExtremaDistance";

	private final List<BigDecimal[]> extrema;
	private BigDecimal maxDistance;

	@Override
	public String getName() {
		return NAME;
	}

	@Override
	public BigDecimal getValue(Population population) {
		final List<BigDecimal> resultList = new ArrayList<BigDecimal>(); 
		BigDecimal closestExtremumX = null;
		BigDecimal closestExtremumY = null;
		for (final Specimen specimen : population) {
			BigDecimal minExtremaDistance = BigDecimal.valueOf(Long.MAX_VALUE);
			for (final BigDecimal[] extremum : extrema) {
				final BigDecimal distance = getDistance(extremum[0],
						extremum[1], BigDecimal.valueOf(specimen.getGenotype()
								[0].asDouble()), BigDecimal.valueOf(specimen
										.getGenotype()[0].asDouble()));
				if (distance.compareTo(minExtremaDistance) <= 0) {
					minExtremaDistance = distance;
					closestExtremumX = extremum[0];
					closestExtremumY = extremum[1];
				}
			}
			logger.trace("closest extremum: [" + closestExtremumX + ", " + closestExtremumY + "]");
			logger.trace("distance to specimen [" + specimen.getGenotype()[0]+ ", " + specimen.getGenotype()[1] + "] is " + minExtremaDistance);
			resultList.add(minExtremaDistance);
		}
		BigDecimal result = BigDecimal.ZERO;
		for (final BigDecimal dist : resultList) {
			result = result.add(dist);
		}
		result = result.divide(BigDecimal.valueOf(resultList.size()), RoundingMode.HALF_DOWN);
		logger.trace("min extrema distance metric value: " + result);
		return result;
	}

	private BigDecimal getDistance(BigDecimal x1, BigDecimal y1, BigDecimal x2,
			BigDecimal y2) {
		BigDecimal firstPart = (x1.subtract(x2)).pow(2);
		final BigDecimal secondPart = (y1.subtract(y2)).pow(2);
		firstPart = firstPart.add(secondPart);
		return BigDecimal.valueOf(Math.sqrt(firstPart.doubleValue()));
	}

	public MinExtremaDistanceMetric(List<BigDecimal[]> extrema) {
		this.extrema = extrema;
	}

	@Override
	public BigDecimal getMaxValue() {
		return maxDistance;
	}
}
